package cn.iocoder.yudao.module.system.service.equivalentscorerank;

import cn.iocoder.yudao.module.system.dal.dataobject.equivalentscorerank.EquivalentScoreRankDO;
import cn.iocoder.yudao.module.system.dal.dataobject.equivalentscorerank.EquivalentScoreDetailDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examcontrolline.ExamControlLineDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examscore.ExamScoreDO;
import cn.iocoder.yudao.module.system.dal.dataobject.gugu.ProvinceScoreDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.equivalentscorerank.EquivalentScoreRankMapper;
import cn.iocoder.yudao.module.system.dal.mysql.equivalentscorerank.EquivalentScoreDetailMapper;
import cn.iocoder.yudao.module.system.service.examcontrolline.ExamControlLineService;
import cn.iocoder.yudao.module.system.service.examscore.ExamScoreService;
import cn.iocoder.yudao.module.system.service.gugu.MajorRecommendationService;
import cn.iocoder.yudao.module.system.service.gugu.ProvinceScoreService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.util.SubjectSelectionUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 等效位次和等效分 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class EquivalentScoreRankServiceImpl implements EquivalentScoreRankService {

    @Resource
    private EquivalentScoreRankMapper equivalentScoreRankMapper;
    
    @Resource
    private EquivalentScoreDetailMapper equivalentScoreDetailMapper;
    
    @Resource
    private ExamScoreService examScoreService;
    
    @Resource
    private ExamControlLineService examControlLineService;
    
    @Resource
    private ProvinceScoreService provinceScoreService;
    
    @Resource
    private MajorRecommendationService majorRecommendationService;

    @Resource
    AdminUserService userService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public EquivalentScoreRankDO calculateEquivalentScoreRank(Long examId, Long studentId, Long controlLineId) {
        log.info("开始计算等效位次和等效分：examId={}, studentId={}, controlLineId={}", examId, studentId, controlLineId);
        
        try {
            // 1. 验证前置条件
            String validationMessage = validateCalculationPreconditions(examId, studentId, controlLineId);
            if (validationMessage != null) {
                log.warn("等效位次和等效分计算前置条件验证失败：{}", validationMessage);
                return createFailedRecord(examId, studentId, controlLineId, validationMessage);
            }
            
            // 2. 获取学生成绩
            ExamScoreDO examScore = examScoreService.getExamScoreByExamIdAndStudentId(examId, studentId);
            if (examScore == null) {
                String errorMsg = "未找到学生成绩记录";
                log.warn(errorMsg);
                return createFailedRecord(examId, studentId, controlLineId, errorMsg);
            }
            
            // 3. 获取特控线信息
            ExamControlLineDO controlLine = examControlLineService.getExamControlLine(controlLineId);
            if (controlLine == null) {
                String errorMsg = "未找到特控线配置";
                log.warn(errorMsg);
                return createFailedRecord(examId, studentId, controlLineId, errorMsg);
            }
            
            // 4. 获取省控线数据
            String province = controlLine.getProvince();
            String examType = determineExamType(examScore); // 根据成绩判断考试类型（物理类/历史类）
            
            // 优先使用特控线配置中已计算的省控线
            BigDecimal currentProvinceControlLine;
            if ("物理类".equals(examType)) {
                currentProvinceControlLine = controlLine.getPhysicsProvinceControlLine();
            } else if ("历史类".equals(examType)) {
                currentProvinceControlLine = controlLine.getHistoryProvinceControlLine();
            } else {
                // 如果无法确定科目类型，优先使用物理类省控线
                currentProvinceControlLine = controlLine.getPhysicsProvinceControlLine();
                if (currentProvinceControlLine == null) {
                    currentProvinceControlLine = controlLine.getHistoryProvinceControlLine();
                }
            }
            
            // 如果特控线配置中没有省控线，则从外部服务获取
            List<Integer> years = Arrays.asList(2023, 2024, 2025);
            Map<Integer, BigDecimal> provinceControlLines = new HashMap<>();
            
            if (currentProvinceControlLine != null) {
                // 使用特控线配置中的省控线作为当前年份的省控线
                Integer currentYear = controlLine.getYear();
                if (currentYear != null) {
                    provinceControlLines.put(currentYear, currentProvinceControlLine);
                }
            }
            
            // 补充获取其他年份的省控线数据
            Map<Integer, BigDecimal> externalProvinceControlLines = getProvinceControlLines(province, examType, years);
            for (Map.Entry<Integer, BigDecimal> entry : externalProvinceControlLines.entrySet()) {
                if (!provinceControlLines.containsKey(entry.getKey())) {
                    provinceControlLines.put(entry.getKey(), entry.getValue());
                }
            }
            
            if (provinceControlLines.isEmpty()) {
                String errorMsg = "未找到省控线数据";
                log.warn(errorMsg);
                return createFailedRecord(examId, studentId, controlLineId, errorMsg);
            }
            
            // 5. 计算等效分和等效位次
            // 优先使用赋分字段total_score_assigned，如果没有则使用原始分total_score
            BigDecimal originalScore = examScore.getTotalScoreAssigned() != null ? 
                examScore.getTotalScoreAssigned() : examScore.getTotalScore();
            
            // 根据学生科目类型选择对应的特控线
            BigDecimal controlLineScore;
            if ("物理类".equals(examType)) {
                controlLineScore = controlLine.getPhysicsScoreLine();
                if (controlLineScore == null) {
                    String errorMsg = "特控线配置中未设置物理类分数线";
                    log.warn(errorMsg);
                    return createFailedRecord(examId, studentId, controlLineId, errorMsg);
                }
            } else if ("历史类".equals(examType)) {
                controlLineScore = controlLine.getHistoryScoreLine();
                if (controlLineScore == null) {
                    String errorMsg = "特控线配置中未设置历史类分数线";
                    log.warn(errorMsg);
                    return createFailedRecord(examId, studentId, controlLineId, errorMsg);
                }
            } else {
                // 如果无法确定科目类型，优先使用物理类，其次历史类，最后使用原有的scoreLine
                controlLineScore = controlLine.getPhysicsScoreLine();
                if (controlLineScore == null) {
                    controlLineScore = controlLine.getHistoryScoreLine();
                }
                if (controlLineScore == null) {
                    controlLineScore = controlLine.getScoreLine();
                }
                if (controlLineScore == null) {
                    String errorMsg = "特控线配置中未设置任何分数线";
                    log.warn(errorMsg);
                    return createFailedRecord(examId, studentId, controlLineId, errorMsg);
                }
            }
            
            BigDecimal scoreDifference = originalScore.subtract(controlLineScore);
            
            EquivalentScoreRankDO result = EquivalentScoreRankDO.builder()
                    .examId(examId)
                    .studentId(studentId)
                    .studentNo(examScore.getStudentNo())
                    .studentName(examScore.getStudentName())
                    .originalScore(originalScore)
                    .controlLineId(controlLineId)
                    .controlLineScore(controlLineScore)
                    .province(province)
                    .examType(examType)
                    .scoreDifference(scoreDifference)
                    .calculationDate(LocalDateTime.now())
                    .calculationStatus(1)
                    .build();
            
            // 计算三年的等效分和等效位次
            List<EquivalentScoreDetailDO> scoreDetails = new ArrayList<>();
            for (Integer year : years) {
                BigDecimal provinceControlLine = provinceControlLines.get(year);
                if (provinceControlLine != null) {
                    BigDecimal equivalentScore = calculateEquivalentScore(originalScore, controlLineScore, provinceControlLine);
                    Integer equivalentRank = calculateEquivalentRank(equivalentScore, province, year, examType);
                    
                    // 创建详情记录
                    EquivalentScoreDetailDO detail = EquivalentScoreDetailDO.builder()
                            .year(year)
                            .equivalentScore(equivalentScore)
                            .equivalentRank(equivalentRank)
                            .provinceControlLine(provinceControlLine)
                            .build();
                    scoreDetails.add(detail);
                }
            }
            result.setScoreDetails(scoreDetails);
            
            // 6. 保存或更新记录
            EquivalentScoreRankDO existingRecord = equivalentScoreRankMapper.selectByExamIdAndStudentIdAndControlLineId(examId, studentId, controlLineId);
            if (existingRecord != null) {
                result.setId(existingRecord.getId());
                equivalentScoreRankMapper.updateById(result);
                // 删除旧的详情记录
                equivalentScoreDetailMapper.deleteByEquivalentScoreRankId(result.getId());
                log.info("更新等效位次和等效分记录成功：id={}", result.getId());
            } else {
                equivalentScoreRankMapper.insert(result);
                log.info("创建等效位次和等效分记录成功：id={}", result.getId());
            }
            
            // 保存详情记录
            if (!scoreDetails.isEmpty()) {
                for (EquivalentScoreDetailDO detail : scoreDetails) {
                    detail.setEquivalentScoreRankId(result.getId());
                }
                equivalentScoreDetailMapper.insertBatch(scoreDetails);
                log.info("保存等效分数详情记录成功：count={}", scoreDetails.size());
            }
            
            return result;
            
        } catch (Exception e) {
            log.error("计算等效位次和等效分失败：examId={}, studentId={}, controlLineId={}", examId, studentId, controlLineId, e);
            return createFailedRecord(examId, studentId, controlLineId, "计算过程中发生异常：" + e.getMessage());
        }
    }

    @Override
    public int batchCalculateEquivalentScoreRank(Long examId, Long controlLineId) {
        log.info("开始批量计算等效位次和等效分：examId={}, controlLineId={}", examId, controlLineId);
        
        // 获取考试的所有学生成绩
        List<ExamScoreDO> examScores = examScoreService.getExamScoreListByExamId(examId);
        if (examScores.isEmpty()) {
            log.warn("未找到考试成绩记录：examId={}", examId);
            return 0;
        }
        
        int successCount = 0;
        for (ExamScoreDO examScore : examScores) {
            try {
                EquivalentScoreRankDO result = calculateEquivalentScoreRank(examId, examScore.getStudentId(), controlLineId);
                if (result != null && result.getCalculationStatus() == 1) {
                    successCount++;
                }
            } catch (Exception e) {
                log.error("批量计算等效位次和等效分失败：studentId={}", examScore.getStudentId(), e);
            }
        }
        
        log.info("批量计算等效位次和等效分完成：成功={}, 总数={}", successCount, examScores.size());
        return successCount;
    }

    @Override
    public BigDecimal calculateEquivalentScore(BigDecimal originalScore, BigDecimal controlLineScore, BigDecimal provinceControlLineScore) {
        // 等效分 = 学生成绩 - 特控线 + 省控线
        return originalScore.subtract(controlLineScore).add(provinceControlLineScore);
    }

    @Override
    public Integer calculateEquivalentRank(BigDecimal equivalentScore, String province, Integer year, String examType) {
        try {
            // 使用现有的位次计算服务
            return majorRecommendationService.calculateRankByScore(
                    equivalentScore.intValue(), 
                    province, 
                    year, 
                    examType
            );
        } catch (Exception e) {
            log.error("计算等效位次失败：score={}, province={}, year={}, examType={}", 
                    equivalentScore, province, year, examType, e);
            return null;
        }
    }

    @Override
    public Map<Integer, BigDecimal> getProvinceControlLines(String province, String examType, List<Integer> years) {
        Map<Integer, BigDecimal> result = new HashMap<>();
        
        for (Integer year : years) {
            try {
                // 查询省控线数据，这里假设查询一本线作为参考
                List<ProvinceScoreDO> provinceScores = provinceScoreService.getProvinceScoreList(province, year, examType);
                
                // 查找一本线数据
                Optional<ProvinceScoreDO> firstBatchScore = provinceScores.stream()
                        .filter(score -> "特殊类型招生控制线".equals(score.getScoreBatch()) || "特殊".equals(score.getScoreBatch()))
                        .findFirst();
                
                if (firstBatchScore.isPresent()) {
                    result.put(year, BigDecimal.valueOf(firstBatchScore.get().getScore()));
                }
            } catch (Exception e) {
                log.error("获取省控线数据失败：province={}, year={}, examType={}", province, year, examType, e);
            }
        }
        
        return result;
    }

    @Override
    public EquivalentScoreRankDO getEquivalentScoreRank(Long examId, Long studentId) {
        EquivalentScoreRankDO result = equivalentScoreRankMapper.selectByExamIdAndStudentId(examId, studentId);
        loadScoreDetails(result);
        return result;
    }

    @Override
    public EquivalentScoreRankDO getEquivalentScoreRank(Long examId, Long studentId, Long controlLineId) {
        EquivalentScoreRankDO result = equivalentScoreRankMapper.selectByExamIdAndStudentIdAndControlLineId(examId, studentId, controlLineId);
        loadScoreDetails(result);
        return result;
    }

    @Override
    public List<EquivalentScoreRankDO> getEquivalentScoreRanksByStudentId(Long studentId) {
        List<EquivalentScoreRankDO> results = equivalentScoreRankMapper.selectByStudentId(studentId);
        for (EquivalentScoreRankDO result : results) {
            loadScoreDetails(result);
        }
        return results;
    }

    @Override
    public List<EquivalentScoreRankDO> getEquivalentScoreRanksByExamId(Long examId) {
        List<EquivalentScoreRankDO> results = equivalentScoreRankMapper.selectByExamId(examId);
        for (EquivalentScoreRankDO result : results) {
            loadScoreDetails(result);
        }
        return results;
    }

    @Override
    public Long createEquivalentScoreRank(@Valid EquivalentScoreRankDO equivalentScoreRank) {
        equivalentScoreRankMapper.insert(equivalentScoreRank);
        return equivalentScoreRank.getId();
    }

    @Override
    public void updateEquivalentScoreRank(@Valid EquivalentScoreRankDO equivalentScoreRank) {
        equivalentScoreRankMapper.updateById(equivalentScoreRank);
    }

    @Override
    public void deleteEquivalentScoreRank(Long id) {
        // 先删除详情记录
        equivalentScoreDetailMapper.deleteByEquivalentScoreRankId(id);
        // 再删除主记录
        equivalentScoreRankMapper.deleteById(id);
    }

    @Override
    public List<EquivalentScoreDetailDO> getEquivalentScoreDetails(Long equivalentScoreRankId) {
        return equivalentScoreDetailMapper.selectByEquivalentScoreRankId(equivalentScoreRankId);
    }

    @Override
    public EquivalentScoreDetailDO getEquivalentScoreDetail(Long equivalentScoreRankId, Integer year) {
        return equivalentScoreDetailMapper.selectByEquivalentScoreRankIdAndYear(equivalentScoreRankId, year);
    }

    @Override
    public void saveEquivalentScoreDetails(Long equivalentScoreRankId, List<EquivalentScoreDetailDO> details) {
        if (details != null && !details.isEmpty()) {
            // 先删除旧的详情记录
            equivalentScoreDetailMapper.deleteByEquivalentScoreRankId(equivalentScoreRankId);
            // 设置关联ID并保存新的详情记录
            for (EquivalentScoreDetailDO detail : details) {
                detail.setEquivalentScoreRankId(equivalentScoreRankId);
            }
            equivalentScoreDetailMapper.insertBatch(details);
        }
    }

    @Override
    public void deleteEquivalentScoreDetails(Long equivalentScoreRankId) {
        equivalentScoreDetailMapper.deleteByEquivalentScoreRankId(equivalentScoreRankId);
    }

    @Override
    public int deleteEquivalentScoreRanksByExamId(Long examId) {
        return equivalentScoreRankMapper.deleteByExamId(examId);
    }

    @Override
    public int deleteEquivalentScoreRanksByStudentId(Long studentId) {
        return equivalentScoreRankMapper.deleteByStudentId(studentId);
    }

    @Override
    public int recalculateEquivalentScoreRank(String province, String examType) {
        // 获取指定省份和考试类型的所有记录
        List<EquivalentScoreRankDO> records = equivalentScoreRankMapper.selectByProvinceAndExamType(province, examType);
        
        int successCount = 0;
        for (EquivalentScoreRankDO record : records) {
            try {
                calculateEquivalentScoreRank(record.getExamId(), record.getStudentId(), record.getControlLineId());
                successCount++;
            } catch (Exception e) {
                log.error("重新计算等效位次和等效分失败：recordId={}", record.getId(), e);
            }
        }
        
        return successCount;
    }

    @Override
    public String validateCalculationPreconditions(Long examId, Long studentId, Long controlLineId) {
        if (examId == null) {
            return "考试ID不能为空";
        }
        if (studentId == null) {
            return "学生ID不能为空";
        }
        if (controlLineId == null) {
            return "特控线ID不能为空";
        }
        
        // 可以添加更多验证逻辑
        return null;
    }
    
    /**
     * 创建失败记录
     */
    private EquivalentScoreRankDO createFailedRecord(Long examId, Long studentId, Long controlLineId, String errorMessage) {
        EquivalentScoreRankDO failedRecord = EquivalentScoreRankDO.builder()
                .examId(examId)
                .studentId(studentId)
                .controlLineId(controlLineId)
                .calculationDate(LocalDateTime.now())
                .calculationStatus(0)
                .errorMessage(errorMessage)
                .build();
        
        try {
            equivalentScoreRankMapper.insert(failedRecord);
        } catch (Exception e) {
            log.error("保存失败记录异常", e);
        }
        
        return failedRecord;
    }
    
    /**
     * 根据成绩判断考试类型
     */
    private String determineExamType(ExamScoreDO examScore) {
        // 如果成绩记录中有省份和首选科目信息，使用SubjectSelectionUtils计算
        if (examScore.getProvince() != null && !examScore.getProvince().trim().isEmpty()) {
            try {
                // 获取当前年份
                String year = String.valueOf(java.time.Year.now().getValue());
                
                // 确定首选科目
                String firstSubject = examScore.getFirstSubject();
                if (firstSubject == null || firstSubject.trim().isEmpty()) {
                    // 如果没有设置首选科目，根据成绩判断
                    if (examScore.getPhysicsScore() != null && examScore.getPhysicsScore().compareTo(BigDecimal.ZERO) > 0) {
                        firstSubject = "物理";
                    } else if (examScore.getHistoryScore() != null && examScore.getHistoryScore().compareTo(BigDecimal.ZERO) > 0) {
                        firstSubject = "历史";
                    } else {
                        firstSubject = "物理"; // 默认物理
                    }
                }
                
                // 使用SubjectSelectionUtils工具类获取正确的考试类型
                String calculatedExamType = SubjectSelectionUtils.getSubjectSelectionType(examScore.getProvince(), year, firstSubject);
                log.debug("根据省份{}、年份{}、首选科目{}计算得到考试类型：{}", examScore.getProvince(), year, firstSubject, calculatedExamType);
                return calculatedExamType;
            } catch (Exception e) {
                log.warn("使用SubjectSelectionUtils计算考试类型失败：{}", e.getMessage());
            }
        }
        
        // 尝试通过学生ID获取用户信息作为备用方案
        try {
            AdminUserDO user = userService.getUser(examScore.getStudentId());
            if (user != null && user.getProvince() != null) {
                String year = String.valueOf(java.time.Year.now().getValue());
                String firstSubject = user.getFirstSubject();
                if (firstSubject == null || firstSubject.trim().isEmpty()) {
                    if (examScore.getPhysicsScore() != null && examScore.getPhysicsScore().compareTo(BigDecimal.ZERO) > 0) {
                        firstSubject = "物理";
                    } else if (examScore.getHistoryScore() != null && examScore.getHistoryScore().compareTo(BigDecimal.ZERO) > 0) {
                        firstSubject = "历史";
                    } else {
                        firstSubject = "物理";
                    }
                }
                return SubjectSelectionUtils.getSubjectSelectionType(user.getProvince(), year, firstSubject);
            }
        } catch (Exception e) {
            log.warn("获取用户信息失败：{}", e.getMessage());
        }
        
        // 最后的简单判断方法作为兜底
        if (examScore.getPhysicsScore() != null && examScore.getPhysicsScore().compareTo(BigDecimal.ZERO) > 0) {
            return "物理类";
        } else if (examScore.getPoliticsScore() != null && examScore.getPoliticsScore().compareTo(BigDecimal.ZERO) > 0) {
            return "历史类";
        }
        return "物理类"; // 默认值
    }
    
    /**
     * 设置指定年份的等效分和等效位次数据
     */
    /**
     * 加载等效分数详情数据
     *
     * @param equivalentScoreRank 等效分数排名记录
     */
    private void loadScoreDetails(EquivalentScoreRankDO equivalentScoreRank) {
        if (equivalentScoreRank != null && equivalentScoreRank.getId() != null) {
            List<EquivalentScoreDetailDO> details = equivalentScoreDetailMapper.selectByEquivalentScoreRankId(equivalentScoreRank.getId());
            equivalentScoreRank.setScoreDetails(details);
        }
    }

    @Override
    public Map<Integer, Map<String, Object>> calculateEquivalentScoreRankByInput(String province, String examType, BigDecimal examScore, BigDecimal controlLineScore) {
        log.info("开始根据用户输入计算等效位次和等效分：province={}, examType={}, examScore={}, controlLineScore={}", 
                province, examType, examScore, controlLineScore);
        
        Map<Integer, Map<String, Object>> result = new HashMap<>();
        
        try {
            // 定义要计算的年份
            List<Integer> years = Arrays.asList(2025, 2024, 2023);
            
            // 获取各年份的省控线数据
            Map<Integer, BigDecimal> provinceControlLines = getProvinceControlLines(province, examType, years);
            
            // 计算每年的等效分和等效位次
            for (Integer year : years) {
                Map<String, Object> yearResult = new HashMap<>();
                
                BigDecimal provinceControlLine = provinceControlLines.get(year);
                if (provinceControlLine != null) {
                    // 计算等效分：学生成绩 - 特控线 + 省控线
                    BigDecimal equivalentScore = calculateEquivalentScore(examScore, controlLineScore, provinceControlLine);
                    
                    // 计算等效位次
                    Integer equivalentRank = calculateEquivalentRank(equivalentScore, province, year, examType);
                    
                    yearResult.put("equivalentScore", equivalentScore);
                    yearResult.put("equivalentRank", equivalentRank);
                    yearResult.put("provinceControlLine", provinceControlLine);
                    yearResult.put("year", year);
                    
                    log.info("{}年等效分计算结果：等效分={}, 等效位次={}, 省控线={}", 
                            year, equivalentScore, equivalentRank, provinceControlLine);
                } else {
                    log.warn("未找到{}年{}省{}类型的省控线数据", year, province, examType);
                    yearResult.put("error", "未找到" + year + "年省控线数据");
                }
                
                result.put(year, yearResult);
            }
            
        } catch (Exception e) {
            log.error("计算等效位次和等效分失败：province={}, examType={}, examScore={}, controlLineScore={}", 
                    province, examType, examScore, controlLineScore, e);
            
            // 返回错误信息
            Map<String, Object> errorResult = new HashMap<>();
            errorResult.put("error", "计算失败：" + e.getMessage());
            result.put(0, errorResult);
        }
        
        return result;
    }

}